#include <stdio.h>
#include <string.h>
#include "lt_fat32.h"

#define LTFAT_MBR_SECNO 0

typedef char BYTE;
typedef unsigned short u16;
typedef unsigned int   u32;

static int _to_int(char *buf, int len)
{
	int tmp;
	memcpy(&tmp, buf, len);
	return tmp;
}

static void _memcpy_(char *dst, char *src, int len)
{
	if (!dst || !src)
		return ;	

	while (*src != 0x20 && (len --))
	{
		*dst++ = *src ++;
	}
}

int parse_entry_items(FILE_TYPE_t *file, char *buf)
{
	unsigned short last_time = 0;
	unsigned short last_date = 0;
	unsigned short rdate = 0;
	unsigned short cdate = 0;	
	unsigned short ctime = 0;
	unsigned char ctime_u = 0;
	unsigned char *attr = 0;
	char name[16] = {0};

	FILE_ENTRY_t *p = (FILE_ENTRY_t *)buf;	
	if (p->ENTRY_FILE_NAME[0] == 0)
		return -1;

	char tmp[9] = {0};
	char tmp1[4] = {0};
	_memcpy_(tmp, p->ENTRY_FILE_NAME, 8);

	tmp[8] = 0;
	memcpy(tmp1, p->ENTRY_FILE_TAIL, 3);
	tmp1[3] = 0;
	sprintf(name, "%s.%s", tmp, tmp1);
	name[strlen(name)] = 0;
	memcpy(file->FILE_NAME, name, strlen(name)+1);	

	file->FILE_ATTR = p->ENTRY_FILE_ATTR;
        attr = 0;	
	ctime_u = p->ENTRY_CTIME_U;
	ctime = _to_int(&p->ENTRY_CTIME_HMS[0], 2);

	file->CTIME.HOUR = (ctime>>11) & 0x1f;
	file->CTIME.MIN = (ctime>>5) & 0x3f;
	file->CTIME.SEC = (ctime & 0x1f) * 2; 

	cdate = _to_int(&p->ENTRY_CDATE[0], 2);	
	file->CDATE.YEAR = ((cdate>>9) & 0x7f) + 1980;
	file->CDATE.MON = (cdate>>5) & 0xf;
	file->CDATE.DAY =  cdate & 0x1f;

	rdate = _to_int(&p->ENTRY_RDATE[0], 2);
	file->RDATE.YEAR = ((rdate>>9) & 0x7f) + 1980;
	file->RDATE.MON = (rdate>>5) & 0xf;
	file->RDATE.DAY =  rdate & 0x1f;

	last_time = _to_int(&p->ENTRY_LSTIME[0], 2);
	last_date = _to_int(&p->ENTRY_LSDATE[0], 2);

	file->LSTIME.HOUR = (last_time>>11) & 0x1f;
	file->LSTIME.MIN = (last_time>>5) & 0x3f;
	file->LSTIME.SEC = (last_time & 0x1f) * 2;

	cdate = _to_int(&p->ENTRY_CDATE[0], 2);
	file->LSDATE.YEAR = ((last_date>>9) & 0x7f) + 1980;
	file->LSDATE.MON = (last_date>>5) & 0xf;
	file->LSDATE.DAY = last_date & 0x1f;

	file->FILE_SIZE = _to_int(&p->ENTRY_FILE_SIZE[0], 2);

	unsigned int tmpp = _to_int(&p->ENTRY_CUTNO_H[0], 2);
	file->FILE_CUTNO =  (tmpp<<16)&0xffff | _to_int(&p->ENTRY_CUTNO_L[0], 2);	
#if 0
	printf("%d/%d/%d  %d:%d:%d %s %d %s\r\n", file->LSDATE.YEAR, file->LSDATE.MON, file->LSDATE.DAY,
			file->LSTIME.HOUR, file->LSTIME.MIN, file->LSTIME.SEC,
			attr, file->FILE_SIZE, file->FILE_NAME);
#endif
}

int search_file(char *buf, char *file_name, FILE_TYPE_t *file)
{
	char *ptr = buf;
	int ii;
	for (ii=0; ii<512/32; ii ++, ptr += 32)
	{
		parse_entry_items(file, ptr);
		if (strcasecmp(file->FILE_NAME, file_name) == 0)
		{
			printf("search file %s\n", file->FILE_NAME);	
			return 0;	
		}		
		memset(file, 0, sizeof (FILE_TYPE_t));
	}
	return -1;
}

int open_dir(FAT32_t *fat, int cutno, char *path, HANDLE_t *handle)
{
	unsigned char *ptr = path;
	printf("input path %s\r\n", path);
	char tmp[16] = {0};
	char *p_tmp = tmp;
	char buf[512];

	read_sector(cutno, buf, 1);

	if (!strcmp(path, "/") || !strcmp(path, "//"))
	{
		handle->cutno = fat->root_at;
		handle->type = 0x1;
		return 0; 
	}
	else
	{
		FILE_TYPE_t file;
		search_file(buf, path, &file); 
		handle->cutno = file.FILE_CUTNO; 
		handle->type = file.FILE_ATTR;
	}

	return 0;
}

int file_struct[16];

static int cutno_mappto_secno(FAT32_t *fat, int cutno)
{
     return fat->root_at + fat->secs_per_cut * (cutno-2);
}


static char get_driver_label(char *path, char *drv_label)
{

	if (path[1] == ':')
	{
	    drv_label[0] = path[0];
	    return 0;
	}
	
	if (path[2] == ':')
	{
	   memcpy(drv_label, path, 2);
	   drv_label[2] = 0;
           return 0;	
	}
}

struct GOOD_TEST
{
	char *label;
	DRIVER_t *drv;
	FAT32_t *fat;
}TEST;

int lt_open(unsigned char *file_path)
{           
	HANDLE_t hand;
	char path[64] = {0};
  	char drv_lbl[8] = {0};
        get_driver_label(file_path, drv_lbl);
	printf("lb=%s\r\n", drv_lbl);	    
  
	FAT32_t *fat = TEST.fat;	
#if 1
	if (file_path[0] == '/')
	{
		memcpy(path, &file_path[1], strlen(file_path)-1);
		open_dir(fat, fat->root_at, path, &hand);
	}

	printf("hand->cutno=%d, type=0x%x\r\n", hand.cutno, hand.type);     
	printf("file offset @%d \r\n", fat->root_at + fat->secs_per_cut * (hand.cutno-2));
	file_struct[0] = fat->root_at + fat->secs_per_cut * (hand.cutno-2);
#endif
	return 0;
}

int lt_read(int fd, char *buf, int len)
{
	unsigned int cutno = file_struct[fd];
	read_sector(cutno, buf, len);
}

int parse_dbr(DRIVER_t *drv, FAT32_t *fat)
{
	unsigned int sec = 0;
	unsigned int part_size = 0;
	unsigned int fat_table_cnt = 0;
	unsigned int sectors_per_fat = 0;
	fat->root_at = 0;
	fat->fat1_at = 0;
	fat->fat2_at = 0; 

	memcpy(&sec, (void *)&fat->mbr.mbr_part_info[0].MBR_LBA_SEC[0], 4);
	memcpy(&part_size, &fat->mbr.mbr_part_info[0].MBR_PART_SIZE[0], 4);

	printf("[MBR] offset=%d,  %d sectors\r\n", part_size, sec);

	char buf[512];
	drv->read_sector(sec, buf, 1);
	DBR_t *ptr = (DBR_t *)buf;
	memcpy(&fat->dbr0, ptr, sizeof (DBR_t));

	fat_table_cnt = ptr->DBR_FAT_TABLE_CNT[0];
	sectors_per_fat = _to_int(&ptr->DBR_FAT_SECS[0], 4);

	fat->fat1_at =  _to_int(&ptr->DBR_REVSERD_SECS[0],2) + _to_int(&ptr->DBR_SEC_USED[0], 4); 
	fat->fat2_at = fat->fat1_at + sectors_per_fat;
	fat->root_at = fat->fat2_at + sectors_per_fat;
	printf("[DBR] %d %d %d\r\n", fat->root_at, fat->fat1_at, fat->fat2_at);
	fat->secs_per_cut = ptr->DBR_CLUSTER_SECS[0];
	printf("[DBR] %d CLUSTER_SECS\r\n", fat->secs_per_cut);
	return 0;
}

void parse_mbr(DRIVER_t *drv, FAT32_t *fat)
{
	char buf[512];
	
	drv->read_sector(LTFAT_MBR_SECNO, buf, 1);
	ptarr(buf, 512);
	MBR_t *p = (MBR_t*)buf;
	memcpy((void *)&fat->mbr, (void *)p, sizeof (MBR_t));
	return ;
}


int mount(DRIVER_t *drv)
{
	printf("name=%s\r\n", drv->LT_DRIVER_NAME);

	static FAT32_t tmp;
	TEST.fat = &tmp;
	TEST.drv = drv;
	TEST.label = "C:/";//DRIVER_LABEL;	
	drv->init();
	parse_mbr(drv, &tmp);
	parse_dbr(drv, &tmp);
	
        return 0;
}

